package buscador;

import indexacion.Indice;
import indexacion.StopWords;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author marco
 */
public class Consulta {

    String cadenaBusqueda;
    ArrayList<String> tokensConsulta;
    ArrayList<Float> frecuenciasInversasC;
    int N; // Numero de tokens de la consulta

    public Consulta(String entradaBuscador,StopWords stopWords) {
        TokensString separadorPalabras = new TokensString();
        cadenaBusqueda = entradaBuscador;
        tokensConsulta = new ArrayList<>();
        this.analizarLinea(separadorPalabras, stopWords, entradaBuscador);
        N = this.tokensConsulta.size();
        frecuenciasInversasC = new ArrayList<>(N);
    }
    
    public String getCadenaBusqueda() {
       return this.cadenaBusqueda;
    }
    private int analizarLinea(TokensString segmentacion, StopWords stopWords, String linea) {

        ReduccionRaiz RedRaiz = new ReduccionRaiz();

        String expresionSignosPuntIzq = "[,;\\:\\(¿¡'\\[\\{\\.]*";
        String expresionSignosPuntDer = "[,;\\:\\)?!'\\]\\}\\.]*";

        String patronTokenPalabras = "(" + expresionSignosPuntIzq + "\\p{Alpha}+" + expresionSignosPuntDer + ")+";

        String patronTokenAbreviaturasCompuesto = "(\\p{Alpha}\\.){2,}+";

        String patronTokenPorcentajesEnteros = "(" + expresionSignosPuntIzq + "\\p{Digit}+%" + expresionSignosPuntDer + ")+";
        String patronTokenPorcentajesDecimales = "(" + expresionSignosPuntIzq + "[0-9]+[.,]{1}[0-9]+%" + expresionSignosPuntDer + ")+";

        String patronTokenNumerosEnteros = "(" + expresionSignosPuntIzq + "\\p{Digit}+" + expresionSignosPuntDer + ")+";
        String patronTokenNumerosDecimales = "(" + expresionSignosPuntIzq + "[0-9]+[.,]{1}[0-9]+" + expresionSignosPuntDer + ")+";
        String patronTokenOrdinales = "(" + expresionSignosPuntIzq + "[(\\p{Digit}+st)(\\p{Digit}+ST)(\\p{Digit}+nd)(\\p{Digit}+ND)(\\p{Digit}+rd)(\\p{Digit}+RD)(\\p{Digit}+th)(\\p{Digit}+TH)]" + expresionSignosPuntDer + ")+";

        String patronTokenIntervalosA = "(" + expresionSignosPuntIzq + "[\\p{Alpha}\\p{Digit}]+\\-[\\p{Alpha}\\p{Digit}]+" + expresionSignosPuntDer + ")+";
        String patronTokenIntervalosB = "(" + expresionSignosPuntIzq + "[0-9]+[.,]{1}[0-9]+-[\\p{Alpha}\\p{Digit}]+" + expresionSignosPuntDer + ")+";
        String patronTokenIntervalosC = "(" + expresionSignosPuntIzq + "[\\p{Alpha}\\p{Digit}]+\\-[0-9]+[.,]{1}[0-9]+" + expresionSignosPuntDer + ")+";
        String patronTokenIntervalosD = "(" + expresionSignosPuntIzq + "[0-9]+[.,]{1}[0-9]+\\-[0-9]+[.,]{1}[0-9]+" + expresionSignosPuntDer + ")+";

        String patronTokenProporcionesA = "(" + expresionSignosPuntIzq + "[(\\p{Alpha}+|\\p{Digit}+|\\p{Alpha}+.)/(\\p{Alpha}+|\\p{Digit}+|\\p{Alpha}+.)]+" + expresionSignosPuntDer + ")+";
        String patronTokenProporcionesB = "(" + expresionSignosPuntIzq + "[\\p{Alpha}\\p{Digit}]+/[\\p{Alpha}\\p{Digit}]+(?:\\-[\\p{Alpha}\\p{Digit}]+)*" + expresionSignosPuntDer + ")+";
        String patronTokenConceptoMedico = "(" + expresionSignosPuntIzq + "/(\\p{Alpha}+[ ]*)+\\.{0,1}/" + expresionSignosPuntDer + ")+";

        Pattern patronAbreviaturasCompuesto = Pattern.compile("((?:\\p{Alpha}\\.){2,}+)");

        Pattern patronPalabras = Pattern.compile("(\\p{Alpha}+)"); // Para filtrar palabras compuestas solo por letras
        Pattern patronConceptoMedico = Pattern.compile("(\\p{Alpha}+)"); // Para filtrar palabras en los conceptos medicos
        Pattern patronPorcentajesEnteros = Pattern.compile("(\\p{Digit}+%)"); // Para filtrar porcentajes enteros
        Pattern patronPorcentajesDecimales = Pattern.compile("([0-9]+[.,]{1}[0-9]+%)"); // Para filtrar porcentajes enteros
        Pattern patronNumerosEnteros = Pattern.compile("([\\-\\+]{0,1}[\\p{Digit}+(?:\\p{Digit}+\\.\\p{Digit}+)]+)"); // Para filtrar numeros enteros
        Pattern patronNumerosDecimales = Pattern.compile("([\\-\\+]{0,1}[0-9]+[.,]{1}[0-9]+)"); // Para filtrar numeros decimales
        Pattern patronOrdinales = Pattern.compile("(\\p{Digit}+st|\\p{Digit}+ST|\\p{Digit}+nd|\\p{Digit}+ND|\\p{Digit}+rd|\\p{Digit}+RD|\\p{Digit}+th|\\p{Digit}+TH)"); // Para filtrar ordinales
        Pattern patronIntervalosA = Pattern.compile("([\\p{Alpha}\\p{Digit}]+\\-[\\p{Alpha}\\p{Digit}]+(\\-[\\p{Alpha}\\p{Digit}]+)*)");
        Pattern patronIntervalosB = Pattern.compile("(\\p{Digit}+[.,]{1}\\p{Digit}+\\-[\\p{Alpha}\\p{Digit}]+)");
        Pattern patronIntervalosC = Pattern.compile("([\\p{Alpha}\\p{Digit}]+\\-[0-9]+[.,]{1}[0-9]+)");
        Pattern patronIntervalosD = Pattern.compile("([0-9]+[.,]{1}[0-9]+\\-[0-9]+[.,]{1}[0-9]+)");
        Pattern patronProporcionesA = Pattern.compile("((\\p{Alpha}+|\\p{Digit}+|\\p{Alpha}+.)/(\\p{Alpha}+|\\p{Digit}+|\\p{Alpha}+.))");
        Pattern patronProporcionesB = Pattern.compile("([\\p{Alpha}\\p{Digit}]+/[\\p{Alpha}\\p{Digit}]+(?:\\-[\\p{Alpha}\\p{Digit}]+)*)");

        String token;
        Matcher m;
        int j = 0;
        int numTokensLinea;

        ArrayList<String> campos = segmentacion.getTokens(linea);
        numTokensLinea = campos.size();

        while (j < numTokensLinea) {

            token = campos.get(j++);
            
            // Se comprueba si es una abreviatura compuesta
            if (token.matches(patronTokenAbreviaturasCompuesto)) {
                m = patronAbreviaturasCompuesto.matcher(token);
                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else
            // Se comprueba si es un grupo de letras
            if (token.matches(patronTokenPalabras)) {
                m = patronPalabras.matcher(token);
                while (m.find()) {

                    // Se incluye la palabra solamente si no es una stop word
                    if (!stopWords.buscaStopWord(m.group(1))) {
                        for (int c = 0; c < m.group(1).length(); c++) {
                            RedRaiz.add(m.group(1).charAt(c));
                        }
                        RedRaiz.stem();
                        token = RedRaiz.toString();
                        tokensConsulta.add(token);
                    }
                }
            } else 
            // Se comprueba si es un concepto médico, palabras encerradas entre /
            if (token.matches(patronTokenConceptoMedico)) {
                m = patronConceptoMedico.matcher(token);
                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else  // Se comprueba si es un decimal 
            if (token.matches(patronTokenNumerosDecimales)) {
                m = patronNumerosDecimales.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un porcentaje decimal
            if (token.matches(patronTokenPorcentajesDecimales)) {

                m = patronPorcentajesDecimales.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un numero entero
            if (token.matches(patronTokenNumerosEnteros)) {
                m = patronNumerosEnteros.matcher(token);

                while (m.find()) {

                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un numero ordinal
            if (token.matches(patronTokenOrdinales)) {
                m = patronOrdinales.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un intervalo
            if (token.matches(patronTokenIntervalosA)) {
                m = patronIntervalosA.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un intervalo
            if (token.matches(patronTokenIntervalosB)) {
                m = patronIntervalosB.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un intervalo
            if (token.matches(patronTokenIntervalosC)) {
                m = patronIntervalosC.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un intervalo
            if (token.matches(patronTokenIntervalosD)) {
                m = patronIntervalosD.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es una proporcion   
            if (token.matches(patronTokenProporcionesA)) {
                m = patronProporcionesA.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es una proporcion   
            if (token.matches(patronTokenProporcionesB)) {
                m = patronProporcionesB.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            } else // Se comprueba si es un porcentaje entero
            if (token.matches(patronTokenPorcentajesEnteros)) {
                m = patronPorcentajesEnteros.matcher(token);

                while (m.find()) {
                    token = m.group(1);
                    tokensConsulta.add(token);
                }
            }
        }
        return numTokensLinea;
    }

    public ArrayList<Float> getFrecuenciasInversas() {
        return this.frecuenciasInversasC;
    }
    public ArrayList<String> getTokensConsulta() {
        return this.tokensConsulta;
    }

    public int getNumTokensConsulta() {
        return this.tokensConsulta.size();
    }

    // Calcula los pesos normalizados de los terminos de la consulta
    public ArrayList<Float> getPesosConsulta(Indice indice) {

        ArrayList<Float> pesosConsulta = new ArrayList<>(N);
        ArrayList<Float> frecuenciasTokensConsulta = new ArrayList<>(N);
        String termino;
        int frecuencia, maxFrecConsulta = 0,numEntradas;
        float tfConsulta;
        float idfConsulta;
        float pesoConsulta;
     
        int n = indice.getNumDocumentosCol(); // Numero de documentos en la coleccion

        // Primero calculamos las frecuencias de los terminos en la consulta
        for (int i = 0; i < N; i++) {
            termino = this.tokensConsulta.get(i);
            frecuencia = 1;
            // Cuenta las veces que aparece el token i en la consulta
            for (int j = 0; j < N; j++) {
                if (termino.equals(tokensConsulta.get(j)) && (j!=i)) {
                    frecuencia++;
                }
            }
            
            // Se añade la frecuencia calculada para el termino i
            frecuenciasTokensConsulta.add((float) frecuencia);
            // Se actualiza la maxima frecuencia de aparicion en la consulta
            if (maxFrecConsulta < frecuencia) {
                maxFrecConsulta = frecuencia;
            }
        }
        // Se calculan ahora las frecuencias y pesos de la consulta segun la formula
        // sugerida por Salton y Buckley
        for (int k = 0; k < N; k++) {
            tfConsulta = (float) (0.5 + ((float) 0.5 * frecuenciasTokensConsulta.get(k) / (float) maxFrecConsulta));
            numEntradas = indice.getNumEntradasToken(tokensConsulta.get(k));
            if (0<numEntradas) {
               // auxiliares.Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", n + "y "+numEntradas);
               idfConsulta = (float) Math.log10(((float)n) / ((float)numEntradas ));
            }
            else
                idfConsulta = 0;
            frecuenciasInversasC.add(idfConsulta);
            pesoConsulta = tfConsulta * idfConsulta;
            pesosConsulta.add(k, pesoConsulta);
            //auxiliares.Log.registrarTexto("/home/marco/Escritorio/BuscadorWEB", frecuenciasTokensConsulta.get(k) + " tiene peso " + pesoConsulta+ ", tf es " + tfConsulta + ", idf " + idfConsulta);
        }

        return pesosConsulta;
    }
}
